import {
  GM_registerMenuCommand,
  GM_addStyle,
  GM_getResourceText,
  GM_setClipboard,
  GM_download,
  GM_openInTab,
  GM_getValue,
  GM_setValue,
  unsafeWindow,
} from "tm";
import Swal from "sweetalert2";
import catchBoxStyle from "./catch-box.css";
import mainStyle from "./main.css";
import { showButton } from "./button.mts";
import {
  type DestinationUrl,
  determineType,
  filtUrls,
  createLuLuButton,
  getBaseUrl,
  howLong,
} from "./utilities.mts";

let vcUrls: DestinationUrl[] = [];
const isFrame = unsafeWindow.self !== unsafeWindow.top,
  convertUrl = "https://tools.thatwind.com/tool/m3u8downloader",
  playerUrl = "https://lemon399.gitlab.io/page/art.html",
  storeKey = "vCatchManifest";

function stdVideoPush(url: string, time?: number) {
  let urlo: URL | null = null;
  if (url.startsWith("http")) {
    urlo = new URL(url);
  } else if (!url.includes(":")) {
    urlo = new URL(url, getBaseUrl(location.href));
  }
  if (urlo) {
    const type = urlo.pathname.endsWith(".m3u8")
        ? 0
        : urlo.pathname.endsWith(".flv")
          ? 2
          : 3,
      vo: DestinationUrl = { type, url, time };
    isFrame ? unsafeWindow.top?.postMessage(vo, "*") : vcUrls.push(vo);
  }
}

function getVideoElsUrl() {
  for (const element of document.querySelectorAll("video")) {
    const time = Number.isFinite(element.duration) ? element.duration : 0;
    stdVideoPush(element.src, time);
    for (const sel of element.querySelectorAll("source")) {
      stdVideoPush(sel.src, time);
    }
  }
}

async function saveList(resp: ExtendedResponse) {
  const destination: DestinationUrl | null =
    resp.responseText && typeof (await resp.responseText) == "string"
      ? determineType(resp.finalUrl, await resp.responseText)
      : resp.response && typeof (await resp.response) == "string"
        ? determineType(resp.finalUrl, (await resp.response) as string)
        : resp.text && typeof (await resp.text) == "string"
          ? determineType(resp.finalUrl, await resp.text)
          : null;
  if (destination && destination.type !== 3) {
    isFrame
      ? unsafeWindow.top?.postMessage(destination, "*")
      : vcUrls.push(destination);
  }
}

function findVideoElement() {
  getVideoElsUrl();
  const frames = unsafeWindow.frames;
  for (let index = 0; index < frames.length; index++) {
    frames[index].postMessage("getVideo", "*");
  }
}

async function showSwal() {
  findVideoElement();
  vcUrls = filtUrls(vcUrls);
  await (vcUrls.length > 0
    ? Swal.fire({
        html: `<section id="vCatchList"></section>`,
        showConfirmButton: false,
        didOpen: () => {
          const container = Swal.getHtmlContainer()
            ?.querySelector("#vCatchList")
            ?.attachShadow({ mode: "open" });
          if (container) {
            container.innerHTML = `<style>${
              GM_getResourceText("luBtn")?.replace(/rem/g, "em") ?? ""
            }${catchBoxStyle}</style>`;
            for (const vo of vcUrls) {
              if (vo.url && typeof vo.type == "number") {
                const typeString =
                  vo.type === 0 ? "m3u8" : vo.type === 1 ? "mpd" : "video";
                const urlo = new URL(vo.url);
                const box = document.createElement("section");
                box.className = `vCatchBox vCatchType_${typeString}`;
                box.innerHTML = `<p>
              <span class="vCatchSpan">${vo.url}</span>
              <span class="vCatchTime">${howLong(vo.time)}</span>
              </p>`;
                const bar = document.createElement("section");
                bar.className = "vCatchBar";
                bar.append(
                  createLuLuButton("复制地址", "warn", () => {
                    GM_setClipboard(vo.url);
                  }),
                );
                if (vo.type === 3 || vo.type === 2) {
                  bar.append(
                    createLuLuButton("下载视频", "safe", () => {
                      const suffix =
                        /\/[^/]+?(\.\w+)?$/.exec(urlo.pathname)?.[1] ?? ".mp4";
                      GM_download(vo.url, document.title + suffix);
                    }),
                  );
                } else if (vo.type === 0) {
                  bar.append(
                    createLuLuButton("下载视频", "safe", () =>
                      GM_openInTab(
                        `${convertUrl}#${new URLSearchParams({
                          m3u8: vo.url,
                          referer: location.href,
                          filename: document.title || "",
                        }).toString()}`,
                      ),
                    ),
                  );
                }
                bar.append(
                  createLuLuButton(
                    "跳转播放",
                    vo.text ? "error" : "primary",
                    () => {
                      if (vo.text) {
                        GM_setValue(storeKey, JSON.stringify(vo));
                        GM_openInTab(`${playerUrl}?frommonkey=1`);
                      } else {
                        GM_openInTab(`${playerUrl}?pure=${vo.url}`);
                      }
                    },
                  ),
                );
                box.append(bar);
                container.append(box);
              }
            }
          }
        },
      })
    : Swal.fire({
        title: "没有发现视频",
        html: "1.2 秒后自动关闭",
        showConfirmButton: false,
        timer: 1200,
        timerProgressBar: true,
      }));
}

if (location.href.startsWith(playerUrl)) {
  if (location.search === "?frommonkey=1") {
    unsafeWindow.addEventListener("load", () => {
      const vo = JSON.parse(GM_getValue(storeKey) as string) as DestinationUrl;
      (unsafeWindow.playVideo as (type: number, url?: string) => void)(
        vo.type,
        vo.text ? URL.createObjectURL(new Blob([vo.text])) : vo.url,
      );
    });
  }
} else {
  const perfObs = new PerformanceObserver((list) => {
    for (const entry of list.getEntriesByType(
      "resource",
    ) as PerformanceResourceTiming[]) {
      const urlo = new URL(entry.name);
      if (
        entry.initiatorType == "video" ||
        entry.entryType == "video" ||
        urlo.pathname.endsWith(".flv")
      )
        stdVideoPush(entry.name);
    }
  });

  if (isFrame) {
    unsafeWindow.addEventListener(
      "message",
      (event: MessageEvent<string>) => {
        if (event.data === "getVideo") {
          getVideoElsUrl();
        }
      },
      false,
    );
  } else {
    unsafeWindow.addEventListener(
      "message",
      (event: MessageEvent<DestinationUrl>) => {
        vcUrls.push(event.data);
      },
      false,
    );

    GM_addStyle(GM_getResourceText("swDark") ?? "");
    GM_addStyle(GM_getResourceText("luBtn") ?? "");
    GM_addStyle(GM_getResourceText("metaFab") ?? "");

    GM_addStyle(mainStyle);

    GM_registerMenuCommand("视频列表", showSwal);
    unsafeWindow.addEventListener("DOMContentLoaded", () => {
      showButton(showSwal);
    });
  }

  ajaxHooker.hook((request) => {
    request.response = (resp) => {
      saveList(resp).catch((error: unknown) => {
        console.error("响应处理失败", error, request, resp);
      });
    };
  });

  if (PerformanceObserver.supportedEntryTypes.includes("resource")) {
    perfObs.observe({ entryTypes: ["resource"] });
  }
}
